#import <appkit/Panel.h>	
#import <objc/List.h>
#import <objc/HashTable.h>
#import <objc/objc-load.h>
#import <streams/streams.h>

#import <stdio.h>
#import <string.h>
#import <stdlib.h>

#import "DynamicApplication.h"

#define	DYN_NO_ERROR		0
#define	DYN_NOT_OBJECT_FILE	1
#define DYN_MULTIPLE_FUNCTION	2
#define DYN_MULTIPLE_OBJECT	3
#define DYN_UNDEFINED_FUNCTION	4
#define DYN_UNDEFINED_OBJECT	5
#define DYN_UNKNOWN_ERROR	6

char	*errorCodes[7] = {	NULL, 
				NULL, 
				"multiple definitions of symbol _",
				"multiple definitions of symbol .objc_class_name",
				"Undefined symbols:\n_",
				"Undefined symbols:\n.objc_class_name",
				NULL							};
				
char	*errorList[7] = {	NULL,
				NULL,
				"The Class being loaded contained functions names which conflicted with those already loaded",
				"The Class being loaded contained Class names which conflicted with those already loaded",
				"The Class being loaded requires function that are not present in the current executable",
				"The Class being loaded requires Classes that are not present in the current executable",
				"An unkown error has occurred at load time"		};
				
@implementation DynamicApplication

void LogInClass( id aClass, id aCategory )
{
    char 	*temp_str;
    const char	*className;
    id		temp;
    extern id	NXApp;
    
    temp = [aClass alloc];
    className = [temp name];
    
    if( [[NXApp _dynamicClassesHashTable] isKey:className] )
    {
        fprintf( stderr, "ERROR: A class by the name of %s has already been loaded...\n", className );
    }
    else
    {
        temp_str = malloc( strlen( className ) + 1 * sizeof( char ) );
	strcpy( temp_str, className );
	[[NXApp _dynamicClassesHashTable] insertKey:temp_str value:aClass];
	[NXApp _incrementDynamicClassesNum];
	[NXApp _setLoadedClassInfo:temp_str :aClass];
    }
    
    [temp free];
}

void LogOutClass( const char *className )
{    
    extern id	NXApp;

    printf("I am logging out %s\n", className);
    
    if( [[NXApp _dynamicClassesHashTable] isKey:className] )
    {
        fprintf( stderr, "ERROR: There is no class %s to be unloaded...\n", className );
    }
    else
    {
	[[NXApp _dynamicClassesHashTable] removeKey:className];
	[NXApp _decrementDynamicClassesNum];
    }
}

+ new
{
    self = [super new];
    [self init];
    return self;
}
 
- init
{
    self = [super init];
    
    dynamicClasses = [[HashTable alloc] initKeyDesc:"*" valueDesc:"@"];
    dynamicClassesNum = 0;
    errorFlags._errorOnLastLoad = NO;
    errorFlags._classLoaded = NO;
    errorFlags._errorType = DYN_NO_ERROR;
    lastClassInfo._className = NULL;
    
    return self;
}
    
- loadClass:(char *)classPath
{
    char 	*objectFileName[2];
    NXStream	*aStream;
    void	(*callback)();
    
    callback = LogInClass;
    
    aStream = NXOpenFile( (fileno( stderr )), NX_READWRITE );
    
    errorFlags._errorOnLastLoad = NO;
    errorFlags._classLoaded = NO;
    errorFlags._errorType = DYN_NO_ERROR;

    if( strcmp( strrchr( classPath, '.' ), ".o" ) )
    {
	errorFlags._errorOnLastLoad = YES;
	errorFlags._classLoaded = NO;
	errorFlags._errorType = DYN_NOT_OBJECT_FILE;
	NXRunAlertPanel("Load Error", "This version of DynamicApplication will only load '.o' object files", 
			"OK", NULL, NULL);
    }
    else
    {
        objectFileName[0] = classPath;
    	objectFileName[1] = NULL;
    
    	if( objc_loadModules( objectFileName, aStream, callback, NULL, NULL ) )
	{
	    errorFlags._errorOnLastLoad = YES;
	    errorFlags._classLoaded = NO;
	    errorFlags._errorType = [self _parseErrorStream:aStream];
	}
    }

    NXClose( aStream );
    
    return [self classWithName:lastClassInfo._className];
}

- loadClass:(char *)classPath withName:(char *)className
{
    char *temp_str;		// Don't free this key (hashTable needs it)
    
    temp_str = malloc( strlen( className ) + 1 * sizeof( char ) );
    strcpy( temp_str, className );
    
    [self loadClass:classPath];
    [dynamicClasses insertKey:temp_str value:[dynamicClasses valueForKey:lastClassInfo._className]];
    lastClassInfo._className = temp_str;
    
    return [self classWithName:className];
}

- getClassNameList:(char **)classList
{
    char	*key;
    id		*value;
    int		count = 0;
    
    NXHashState state = [dynamicClasses initState];
    
    while([dynamicClasses nextState:&state key:&key value:&value])
        classList[count++] = key;
	
    return self;
}
        
- (int)classesNum { return dynamicClassesNum; }

- (char *)lastClassName { return lastClassInfo._className; }
- lastClass { return [self classWithName:[self lastClassName]]; }

- (BOOL)errorOnLastLoad { return errorFlags._errorOnLastLoad; }
- (unsigned int)errorType { return errorFlags._errorType; }
- (BOOL)classLoaded { return errorFlags._classLoaded; }

- classWithName:(char *)className
{
    return [dynamicClasses valueForKey:className];
}

- removeClassWithName:(char *)className
{
    [dynamicClasses removeKey:className];	// cheap, lame, boooo hisssss;
    return self;
}

- free
{
    [dynamicClasses freeObjects];
    [dynamicClasses free];
    free( lastClassInfo._className );
    free( &dynamicClassesNum );
    
    [super free];
    
    return nil;
}

- _dynamicClassesHashTable { return dynamicClasses; }
- _incrementDynamicClassesNum { dynamicClassesNum++; return self; }
- _decrementDynamicClassesNum { dynamicClassesNum--; return self; }

- _setLoadedClassInfo:(char *)className :(Class)aClass
{
    free( lastClassInfo._className );
    lastClassInfo._className = malloc( strlen( className ) + 1 * sizeof( char ) );
    strcpy( lastClassInfo._className, className );
    
    errorFlags._classLoaded = YES;
    
    return self;
}

- (unsigned int)_parseErrorStream:(NXStream *)aStream
{
    char 	*errorBuffer;
    int		i;
    
    errorBuffer = aStream->buf_base;
       
    for( i = 2 ; i < 7 ; i++ )
    	if( strstr( errorBuffer, errorCodes[i] ) )
    	{
	    NXRunAlertPanel( "Load Error", errorList[i], "OK", NULL, NULL );
	    break;
    	}

    if( i == 6 )
        NXRunAlertPanel( "Load Error", errorList[i], "OK", NULL, NULL );
	
    return i;
}

@end
